<template>
  <div ref="containerRef" :class="`${prefixCls}-container`">
    <a-upload
      :headers="headers"
      :multiple="multiple"
      :action="uploadUrl"
      :fileList="fileList"
      :disabled="disabled"
      v-bind="bindProps"
      @remove="onRemove"
      @change="onFileChange"
      @preview="onFilePreview"
    >
      <template v-if="isImageMode">
        <div v-if="!isMaxCount">
          <Icon icon="i-ant-design:plus-outlined" />
          <div class="ant-upload-text">{{ text }}</div>
        </div>
      </template>
      <a-button v-else-if="buttonVisible" :disabled="isMaxCount || disabled">
        <Icon icon="i-ant-design:upload-outlined" />
        <span>{{ text }}</span>
      </a-button>
    </a-upload>
  </div>
</template>

<script lang="ts" setup>
  import { ref, reactive, computed, watch, nextTick, createApp } from "vue"
  import { Icon } from "/@/components/Icon"
  import { getAccessToken } from "/@/utils/cache/module/iam"
  import { uploadUrl } from "/@/api/common/api"
  import { propTypes } from "/@/utils/propTypes"
  import { useMessage } from "/@/hooks/web/useMessage"
  import { createImgPreview } from "/@/components/Preview/index"
  import { useAttrs } from "/@/hooks/core/useAttrs"
  import { useDesign } from "/@/hooks/web/useDesign"
  import { UploadTypeEnum } from "./upload.data"
  import { useGlobSetting } from "/@/hooks/setting"
  import { getFileAccessHttpUrl, isIterable } from "/@/utils/common/compUtils"
  import UploadItemActions from "./components/UploadItemActions.vue"
  import { useUserStore } from "/@/store/modules/user"
  import { ResultEnum, RequestHeaderEnum } from "/@/enums/httpEnum"
  const globSetting = useGlobSetting()

  const { createMessage, createConfirm } = useMessage()
  const { prefixCls } = useDesign("j-upload")
  const userStore = useUserStore()
  const attrs = useAttrs()
  const emit = defineEmits(["change", "success", "update:value"])
  const props = defineProps({
    value: propTypes.oneOfType([propTypes.string, propTypes.array]),
    text: propTypes.string.def("上传"),
    fileType: propTypes.string.def(UploadTypeEnum.all),
    /*这个属性用于控制文件上传的业务路径, 目前不用了*/
    bizPath: propTypes.string.def("temp"),
    // 业务类型
    bizType: propTypes.string,
    /**
     * 是否返回url，
     * true：仅返回url
     * false：返回fileName filePath fileSize
     */
    returnUrl: propTypes.bool.def(true),
    // 最大上传数量
    maxCount: propTypes.number.def(0),
    buttonVisible: propTypes.bool.def(true),
    multiple: propTypes.bool.def(true),
    // 是否显示左右移动按钮
    mover: propTypes.bool.def(true),
    // 是否显示下载按钮
    download: propTypes.bool.def(true),
    // 删除时是否显示确认框
    removeConfirm: propTypes.bool.def(false),
    beforeUpload: propTypes.func,
    disabled: propTypes.bool.def(false),
  })

  const headers = reactive({
    Authorization: "Bearer " + getAccessToken(),
    "x-tenant-id": userStore?.getSelectTenant?.id,
  })
  headers[RequestHeaderEnum.ORIGIN_ENDPOINT] = globSetting.originEndpoint

  const fileList = ref<any[]>([])
  const uploadGoOn = ref<boolean>(true)
  // refs
  const containerRef = ref()
  // 是否达到了最大上传数量
  const isMaxCount = computed(() => props.maxCount > 0 && fileList.value.length >= props.maxCount)
  // 当前是否是上传图片模式
  const isImageMode = computed(() => props.fileType === UploadTypeEnum.image)
  // 合并 props 和 attrs
  const bindProps = computed(() => {
    const bind: any = Object.assign({}, props, attrs?.value)
    bind.name = "file"
    bind.listType = isImageMode.value ? "picture-card" : "text"
    bind.class = [bind.class, { "upload-disabled": props.disabled }]
    bind.data = { bizType: props.bizType, ...bind.data }
    // 并不能中断上传过程
    if (!bind.beforeUpload) {
      bind.beforeUpload = onBeforeUpload
    }
    // 并不能中断上传过程
    // 如果当前是图片上传模式，就只能上传图片
    if (isImageMode.value && !bind.accept) {
      bind.accept = "image/*"
    }
    return bind
  })

  watch(
    () => props.value,
    (val) => {
      if (Array.isArray(val)) {
        if (props.returnUrl) {
          parsePathsValue(val.join(","))
        } else {
          parseArrayValue(val)
        }
      } else {
        parsePathsValue(val)
      }
    },
    { immediate: true }
  )

  watch(fileList, () => nextTick(() => addActionsListener()), { immediate: true })

  const antUploadItemCls = "ant-upload-list-item"

  // Listener
  function addActionsListener() {
    if (!isImageMode.value) {
      return
    }
    const uploadItems = containerRef.value ? containerRef.value.getElementsByClassName(antUploadItemCls) : null
    if (!uploadItems || uploadItems.length === 0) {
      return
    }
    for (const uploadItem of uploadItems) {
      let hasActions = uploadItem.getAttribute("data-has-actions") === "true"
      if (!hasActions) {
        uploadItem.addEventListener("mouseover", onAddActionsButton)
      }
    }
  }

  // 添加可左右移动的按钮
  function onAddActionsButton(event) {
    const getUploadItem = () => {
      if (isIterable(event.path)) {
        for (const path of event.path) {
          if (path.classList.contains(antUploadItemCls)) {
            return path
          } else if (path.classList.contains(`${prefixCls}-container`)) {
            return null
          }
        }
      }
      return null
    }
    const uploadItem = getUploadItem()
    if (!uploadItem) {
      return
    }
    const actions = uploadItem.getElementsByClassName("ant-upload-list-item-actions")
    if (!actions || actions.length === 0) {
      return
    }
    // 添加操作按钮
    const div = document.createElement("div")
    div.className = "upload-actions-container"
    createApp(UploadItemActions, {
      element: uploadItem,
      fileList: fileList,
      mover: props.mover,
      download: props.download,
      emitValue: emitValue,
    }).mount(div)
    actions[0].appendChild(div)
    uploadItem.setAttribute("data-has-actions", "true")
    uploadItem.removeEventListener("mouseover", onAddActionsButton)
  }

  // 解析数据库存储的逗号分割
  function parsePathsValue(paths) {
    if (!paths || paths.length == 0) {
      fileList.value = []
      return
    }
    let list: any[] = []
    for (const item of paths.split(",")) {
      let url = getFileAccessHttpUrl(item)
      list.push({
        uid: uidGenerator(),
        name: getFileName(item),
        status: "done",
        url: url,
        response: { status: "history", message: item },
      })
    }
    fileList.value = list
  }

  // 解析数组值
  function parseArrayValue(array) {
    if (!array || array.length == 0) {
      fileList.value = []
      return
    }
    let list: any[] = []
    for (const item of array) {
      let url = getFileAccessHttpUrl(item.filePath)
      list.push({
        uid: uidGenerator(),
        name: item.fileName,
        url: url,
        status: "done",
        response: { status: "history", message: item.filePath },
      })
    }
    fileList.value = list
  }

  // 文件上传之前的操作
  function onBeforeUpload(file) {
    uploadGoOn.value = true
    if (isImageMode.value) {
      if (file.type.indexOf("image") < 0) {
        createMessage.warning("请上传图片")
        uploadGoOn.value = false
        return false
      }
    }
    // 扩展 beforeUpload 验证
    if (typeof props.beforeUpload === "function") {
      return props.beforeUpload(file)
    }
    return true
  }

  // 删除处理事件
  function onRemove() {
    if (props.removeConfirm) {
      return new Promise((resolve) => {
        createConfirm({
          title: "删除",
          content: `确定要删除这${isImageMode.value ? "张图片" : "个文件"}吗？`,
          iconType: "warning",
          onOk: () => resolve(true),
          onCancel: () => resolve(false),
        })
      })
    }
    return true
  }

  // upload组件change事件
  function onFileChange(info) {
    if (!info.file.status && uploadGoOn.value === false) {
      info.fileList.pop()
    }
    let fileListTemp = info.fileList
    // 限制最大上传数
    if (props.maxCount > 0) {
      let count = fileListTemp.length
      if (count >= props.maxCount) {
        let diffNum = props.maxCount - fileListTemp.length
        if (diffNum >= 0) {
          fileListTemp = fileListTemp.slice(-props.maxCount)
        } else {
          return
        }
      }
    }
    if (info.file.status === "done") {
      if (info.file.response.success) {
        fileListTemp = fileListTemp.map((file) => {
          if (file.response) {
            let reUrl = file.response.message
            file.url = getFileAccessHttpUrl(reUrl)
          }
          return file
        })
      }
    } else if (info.file.status === "error") {
      console.error("info", info)
      const respMsg = info.file?.response?.message
      createMessage.error(respMsg || `${info.file.name} 上传失败.`)
    }
    fileList.value = fileListTemp

    if (info.file.status === "done" && info.file.response.code !== ResultEnum.SUCCESS) {
      createMessage.error(`${info.file.response.message}`)
      fileList.value = []
    }

    if (info.file.status === "done" || info.file.status === "removed") {
      //returnUrl为true时仅返回文件路径
      if (props.returnUrl) {
        handlePathChange(info.file.response)
      } else {
        //returnUrl为false时返回文件名称、文件路径及文件大小
        let newFileList: any[] = []
        for (const item of fileListTemp) {
          if (item.status === "done") {
            let fileJson = {
              fileName: item.name,
              filePath: item.response.message,
              fileSize: item.size,
            }
            newFileList.push(fileJson)
          } else {
            return
          }
        }
        if (info.file.response.code == ResultEnum.SUCCESS) {
          emit("success", newFileList)
        }

        emitValue(newFileList)
      }
    }
  }

  function handlePathChange(response) {
    let uploadFiles = fileList.value
    let path = ""
    if (!uploadFiles || uploadFiles.length == 0 || response.code != ResultEnum.SUCCESS) {
      path = ""
    }
    let pathList: string[] = []
    for (const item of uploadFiles) {
      if (item.status === "done") {
        pathList.push(item.response.message)
      } else {
        return
      }
    }
    if (pathList.length > 0 && response.code == ResultEnum.SUCCESS) {
      path = pathList.join(",")
      emit("success", path)
    }
    emitValue(path)
  }

  // 预览文件、图片
  function onFilePreview(file) {
    if (isImageMode.value) {
      createImgPreview({ imageList: [file.url], maskClosable: true })
    } else {
      window.open(file.url)
    }
  }

  function emitValue(value) {
    emit("change", value)
    emit("update:value", value)
  }

  function uidGenerator() {
    return "-" + parseInt(Math.random() * 10000 + 1, 10)
  }

  function getFileName(path) {
    if (path.lastIndexOf("\\") >= 0) {
      let reg = new RegExp("\\\\", "g")
      path = path.replace(reg, "/")
    }
    return path.substring(path.lastIndexOf("/") + 1)
  }

  defineExpose({
    addActionsListener,
  })
</script>

<style lang="less">
  //noinspection LessUnresolvedVariable
  @prefix-cls: ~"@{namespace}-j-upload";

  .@{prefix-cls} {
    &-container {
      position: relative;

      .upload-disabled {
        .ant-upload-list-item {
          .anticon-close {
            display: none;
          }

          .anticon-delete {
            display: none;
          }
        }

        /* 详情界面 图片下载按钮显示不全*/
        .upload-download-handler {
          right: 6px !important;
        }
      }

      .ant-upload-list-item {
        .upload-actions-container {
          position: absolute;
          top: -31px;
          left: -18px;
          z-index: 11;
          width: 84px;
          height: 84px;
          line-height: 28px;
          text-align: center;
          pointer-events: none;

          a {
            opacity: 0.9;
            margin: 0 5px;
            cursor: pointer;
            transition: opacity 0.3s;

            .anticon {
              color: #fff;
              font-size: 16px;
            }

            &:hover {
              opacity: 1;
            }
          }

          .upload-mover-handler,
          .upload-download-handler {
            position: absolute;
            pointer-events: auto;
          }

          .upload-mover-handler {
            width: 100%;
            bottom: 0;
          }

          .upload-download-handler {
            top: -4px;
            right: -4px;
          }
        }
      }
    }
  }
</style>
